package com.arpa.oms.service.impl;

import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.CommonUtil;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.oms.domain.dto.OmsReceivableDTO;
import com.arpa.oms.domain.dto.OmsSettlementEditDTO;
import com.arpa.oms.domain.dto.OmsSettlementItemEditDTO;
import com.arpa.oms.domain.entity.OmsReceivable;
import com.arpa.oms.domain.entity.OmsReceivableDetail;
import com.arpa.oms.domain.entity.OmsReceivableItem;
import com.arpa.oms.domain.enums.OmsOutboundTypeEnum;
import com.arpa.oms.domain.enums.PayableAndReceivableStatusEnum;
import com.arpa.oms.domain.vo.OmsReceivableVO;
import com.arpa.oms.mapper.OmsReceivableMapper;
import com.arpa.oms.service.IOmsReceivableDetailService;
import com.arpa.oms.service.IOmsReceivableItemService;
import com.arpa.oms.service.IOmsReceivableService;
import com.arpa.wms.common.util.BigDecimalUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

import static com.arpa.oms.domain.enums.PayableAndReceivableStatusEnum.NO_PASS;
import static com.arpa.oms.domain.enums.PayableAndReceivableStatusEnum.PASS;

/**
 * <p>
 * OMS收款单 服务实现类
 * </p>
 *
 * @author LEO
 * @since 2020-11-17
 */
@Service
@Log4j2(topic = "business")
public class OmsReceivableServiceImpl extends ServiceImpl<OmsReceivableMapper, OmsReceivable> implements IOmsReceivableService {


    @Resource
    private PartyCache partyCache;
    @Autowired
    private IOmsReceivableDetailService omsReceivableDetailService;
    @Autowired
    private IOmsReceivableItemService omsReceivableItemService;

    /**
     * 保存
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean save(OmsReceivable entity) {
        if (StringUtils.isBlank(entity.getCode())) {
            entity.setCode(IdUtil.simpleUUID());
        }
        if (StringUtils.isBlank(entity.getCreatedBy())) {
            entity.setCreatedBy(UserUtil.getCode());
        }
        //设置金额类 保留两位小数四舍五入
        entity.setAmountMoney(BigDecimalUtil.getNumber(entity.getAmountMoney()));
        entity.setAmountReduce(BigDecimalUtil.getNumber(entity.getAmountReduce()));
        return super.save(entity);
    }

    /**
     * 根据code更新实体
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int updateByCode(OmsReceivable entity) {
        if (StringUtils.isBlank(entity.getModifiedBy())) {
            entity.setModifiedBy(UserUtil.getCode());
        }
        //设置金额类 保留两位小数四舍五入
        entity.setAmountMoney(BigDecimalUtil.getNumber(entity.getAmountMoney()));
        entity.setAmountReduce(BigDecimalUtil.getNumber(entity.getAmountReduce()));
        return baseMapper.update(entity, new QueryWrapper<OmsReceivable>().lambda().eq(OmsReceivable::getCode, entity.getCode()));
    }

    /**
     * 查询列表
     *
     * @param omsReceivableDTO
     * @return
     */
    @Override
    public List<OmsReceivableVO> queryList(OmsReceivableDTO omsReceivableDTO) {
        //排序字段名需要驼峰转数据库下划线类型字段名
        if (StringUtils.isNotEmpty(omsReceivableDTO.getSortField())) {
            omsReceivableDTO.setSortField(CommonUtil.camel2Underline(omsReceivableDTO.getSortField()));
        }
        List<OmsReceivableVO> omsReceivableVOList = baseMapper.queryList(omsReceivableDTO);
        return omsReceivableVOList;
    }

    /**
     * 查询合计,包含总数
     *
     * @param omsReceivableDTO
     * @return
     */
    @Override
    public OmsReceivableVO queryListSum(OmsReceivableDTO omsReceivableDTO) {
        OmsReceivableVO omsReceivableVO = baseMapper.queryListSum(omsReceivableDTO);
        return omsReceivableVO;
    }

    /**
     * 通过收款单号获取收款单信息，包含收款明细
     *
     * @param code
     * @return
     */
    @Override
    public OmsReceivable getOmsReceivable(String code) {
        OmsReceivable entity = super.getOne(new QueryWrapper<OmsReceivable>().lambda().eq(OmsReceivable::getCode, code)
                .eq(OmsReceivable::getShipmentCode, UserUtil.getShipmentCompanyCode()));
        if (null == entity) {
            log.error("查询收款单【{}】详情失败，找不到收款单信息", code);
            throw new ServiceException("查询收款单【" + code + "】详情失败，找不到收款单信息");
        }

        List<OmsReceivableItem> omsReceivableItems = omsReceivableItemService.listByReceivableCode(code);
        entity.setOmsReceivableItems(omsReceivableItems);
        return entity;
    }



    /**
     * 更新收款单
     * 关联更改费用明细
     *
     * @param settlementEditDTO
     * @return
     */
    @Override
    public void update(OmsSettlementEditDTO settlementEditDTO) {

        // 主单减免金额
        final BigDecimal[] amountReduce = {BigDecimal.ZERO};
        // 主单结算金额
        final BigDecimal[] amountThisSettled = {BigDecimal.ZERO};
        //更新收款单明细
        List<OmsSettlementItemEditDTO> settlementItems = settlementEditDTO.getSettlementItems();
        if (IterUtil.isNotEmpty(settlementItems)) {
            settlementItems.forEach(r -> {
                // 计算减免总金额
                amountReduce[0] = NumberUtil.add(amountReduce[0], r.getReduceMoney());
                // 计算结算总金额
                amountThisSettled[0] = NumberUtil.add(amountThisSettled[0],r.getNewThisSettledMoney());

                //结算单明细
                final OmsReceivableItem omsReceivableItem = omsReceivableItemService.getOne(new QueryWrapper<OmsReceivableItem>().lambda().eq(OmsReceivableItem::getCode, r.getCode()));
                if (null == omsReceivableItem) {
                    log.error("结算单修改失败：结算单【{}】结算明细【{}】找不到", r.getSettlementCode(), r.getCode());
                    throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】找不到");
                }
                // 校验减免金额与结算金额大小
                if (omsReceivableItem.getBillType().getValue().equals(OmsOutboundTypeEnum.SALE_RETURN.getValue())) {
                    // 校验减免金额与结算金额大小
                    if (!NumberUtil.isGreaterOrEqual(r.getReduceMoney(),r.getNewThisSettledMoney())){
                        log.error("结算单修改失败：结算单【{}】结算明细【{}】减免金额需大于等于结算金额", r.getSettlementCode(), r.getCode());
                        throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】减免金额需大于等于结算金额");
                    }
                } else {
                    // 校验减免金额与结算金额大小
                    if (!NumberUtil.isLessOrEqual(r.getReduceMoney(),r.getNewThisSettledMoney())){
                        log.error("结算单修改失败：结算单【{}】结算明细【{}】减免金额需小于等于结算金额", r.getSettlementCode(), r.getCode());
                        throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】减免金额需小于等于结算金额");
                    }
                }


                //费用信息
                OmsReceivableDetail expenseDetail = omsReceivableDetailService.getOne(new QueryWrapper<OmsReceivableDetail>().lambda()
                        .eq(OmsReceivableDetail::getCode, r.getExpenseDetailCode()).eq(OmsReceivableDetail::getShipmentCode, UserUtil.getShipmentCompanyCode()));
                if (expenseDetail == null) {
                    log.error("结算单修改失败：结算单【{}】结算明细【{}】对应的费用信息【{}】找不到", r.getSettlementCode(), r.getCode(), r.getExpenseDetailCode());
                    throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】对应的费用信息【" + r.getExpenseDetailCode() + "】找不到");
                }


                //判断修改后的计算金额是否小于0
                if (r.getNewThisSettledMoney().compareTo(BigDecimal.ZERO) == 0) {
                    log.error("结算单修改失败：结算单【{}】结算明细【{}】结算金额不能等于0", r.getSettlementCode(), r.getCode());
                    throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】结算金额不能等于0");
                }

                //计算收款明细未结算金额
                BigDecimal unsettledMoney =expenseDetail.getAmountMoney().subtract(expenseDetail.getSettledMoney()).subtract(expenseDetail.getWaitingSettledMoney()).add(omsReceivableItem.getThisSettledMoney());

                //未结算金额为负值，则本次结算金额必须小于0
                if(unsettledMoney.compareTo(BigDecimal.ZERO) < 0){
                    if (r.getNewThisSettledMoney().compareTo(BigDecimal.ZERO) > 0) {
                        log.error("结算单修改失败：结算单【{}】结算明细【{}】结算金额必须小于0", r.getSettlementCode(), r.getCode());
                        throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】结算金额必须小于0");
                    }
                }   //未结算金额为正值，则本次结算金额必须大于0
                if(unsettledMoney.compareTo(BigDecimal.ZERO) > 0){
                    if (r.getNewThisSettledMoney().compareTo(BigDecimal.ZERO) < 0) {
                        log.error("结算单修改失败：结算单【{}】结算明细【{}】结算金额必须大于0", r.getSettlementCode(), r.getCode());
                        throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】结算金额必须大于0");
                    }
                }

                if (r.getNewThisSettledMoney().abs().compareTo(unsettledMoney.abs()) > 0) {
                    log.error("结算单修改失败：结算单【{}】结算明细【{}】结算金额不能大于付款明细未结算金额【{}】", r.getSettlementCode(), r.getCode(),unsettledMoney);
                    throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】结算金额不能大于付款明细未结算金额【"+unsettledMoney+"】");
                }



                //还原更新费用信息
                BigDecimal restoreSettledMoney = omsReceivableItem.getThisSettledMoney().subtract(r.getNewThisSettledMoney());
                // 还原减免费用
                BigDecimal restoreReduceMoney = omsReceivableItem.getReduceMoney().subtract(r.getReduceMoney());
                //设置金额类 保留两位小数四舍五入
                restoreReduceMoney = BigDecimalUtil.getNumber(restoreReduceMoney);
                restoreSettledMoney = BigDecimalUtil.getNumber(restoreSettledMoney);
                omsReceivableDetailService.restoreSettlementStatus(expenseDetail.getCode(), restoreSettledMoney, restoreReduceMoney);
                //更新费用明细
                omsReceivableItemService.update(new UpdateWrapper<OmsReceivableItem>().lambda()
                        .eq(OmsReceivableItem::getCode, r.getCode())
                        .set(OmsReceivableItem::getThisSettledMoney, BigDecimalUtil.getNumber(r.getNewThisSettledMoney()))
                        .set(OmsReceivableItem::getReduceMoney, BigDecimalUtil.getNumber(r.getReduceMoney()))
                );


            });
        }


        //要删除的收款单明细
        if (IterUtil.isNotEmpty(settlementEditDTO.getRemoveItemCodes())) {
            List<OmsReceivableItem> omsReceivableItems = omsReceivableItemService.list(new QueryWrapper<OmsReceivableItem>().lambda().in(OmsReceivableItem::getCode, settlementEditDTO.getRemoveItemCodes()));
            if (IterUtil.isNotEmpty(omsReceivableItems)) {
                //获取费用明细
                omsReceivableItems.forEach(r -> {
                    //费用信息
                    OmsReceivableDetail expenseDetail = omsReceivableDetailService.getOne(new QueryWrapper<OmsReceivableDetail>().lambda().eq(OmsReceivableDetail::getCode, r.getReceivableDetailCode()));
                    if (expenseDetail == null) {
                        log.error("结算单修改失败：费用信息【{}】找不到", r.getReceivableDetailCode());
                        throw new ServiceException("结算单删除失败：费用详情【" + r.getReceivableDetailCode() + "】找不到");
                    }
                    //还原更新费用信息
                    omsReceivableDetailService.restoreSettlementStatus(r.getReceivableDetailCode(), BigDecimalUtil.getNumber(r.getThisSettledMoney()), BigDecimalUtil.getNumber(r.getReduceMoney()));
                });
                //删除收款单明细
                omsReceivableItemService.remove(new QueryWrapper<OmsReceivableItem>().lambda().in(OmsReceivableItem::getCode, settlementEditDTO.getRemoveItemCodes()));
            }
        }

        // 主单付款金额
        BigDecimal amountCollected = NumberUtil.sub(amountThisSettled[0],amountReduce[0]);

        //更新结算单主表
        update(new UpdateWrapper<OmsReceivable>().lambda()
                .eq(OmsReceivable::getCode, settlementEditDTO.getCode())
                .eq(OmsReceivable::getShipmentCode, UserUtil.getShipmentCompanyCode())
                .set(OmsReceivable::getRemarks, settlementEditDTO.getRemarks())
                .set(OmsReceivable::getModifiedBy, UserUtil.getCode())
                .set(OmsReceivable::getStatus, settlementEditDTO.getStatus())
                .set(OmsReceivable::getAmountMoney, BigDecimalUtil.getNumber(amountThisSettled[0])) // 结算金额
                .set(OmsReceivable::getAmountReduce, BigDecimalUtil.getNumber(amountReduce[0])) // 减免金额
                .set(OmsReceivable::getAmountCollected, BigDecimalUtil.getNumber(amountCollected)) // 收款金额
                .set(OmsReceivable::getModifiedName, partyCache.translate(UserUtil.getCode()))
        );
    }


    /**
     * 删除收款单
     * 删除收款单的同时，更新费明细状态
     *
     * @param code
     */
    @Override
    public void removeByCode(String code) {
        OmsReceivable omsReceivable = super.getOne(new QueryWrapper<OmsReceivable>().lambda().eq(OmsReceivable::getCode, code).eq(OmsReceivable::getShipmentCode,UserUtil.getShipmentCompanyCode()));

        if (null == omsReceivable) {
            log.error("收款单删除失败：收款单【{}】找不到", code);
            throw new ServiceException("收款单【"+code+"】找不到");
        }
        if (!PayableAndReceivableStatusEnum.NEWS.equals(omsReceivable.getStatus())) {
            log.error("收款单删除失败：收款单【{}】已提交不能删除", code);
            throw new ServiceException("收款单【"+code+"】已提交不能删除");
        }
        //TODO 增加状态判断
        //获取收款单明细
        List<OmsReceivableItem> omsReceivableItems = omsReceivableItemService.list(new QueryWrapper<OmsReceivableItem>().lambda().eq(OmsReceivableItem::getReceivableCode, code));
        //获取费用明细
        omsReceivableItems.forEach(r -> {
            //费用信息
            OmsReceivableDetail expenseDetail = omsReceivableDetailService.getOne(new QueryWrapper<OmsReceivableDetail>().lambda().eq(OmsReceivableDetail::getCode, r.getReceivableDetailCode()));
            if (expenseDetail == null) {
                log.error("结算单修改失败：费用信息【{}】找不到", r.getReceivableDetailCode());
                throw new ServiceException("结算单删除失败：费用详情【" + r.getReceivableDetailCode() + "】找不到");
            }
            //还原更新费用信息
            omsReceivableDetailService.restoreSettlementStatus(r.getReceivableDetailCode(), BigDecimalUtil.getNumber(r.getThisSettledMoney()), BigDecimalUtil.getNumber(r.getReduceMoney()));

        });
        //删除收款单
        super.remove(new QueryWrapper<OmsReceivable>().lambda().eq(OmsReceivable::getCode, code));
        //删除收款单明细
        omsReceivableItemService.remove(new QueryWrapper<OmsReceivableItem>().lambda().eq(OmsReceivableItem::getReceivableCode, code));

    }

    /**
     * 收款单审核
     *
     * @param code   收款单code
     * @param status 审核状态 PASS通过。NO_PASS不通过
     * @param verifyOpinion 审核意见
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean approve(String code, PayableAndReceivableStatusEnum status,String verifyOpinion) {
        String userName = partyCache.translate(UserUtil.getCode());
        update(new UpdateWrapper<OmsReceivable>().lambda().eq(OmsReceivable::getCode, code)
                .set(OmsReceivable::getModifiedBy, UserUtil.getCode())
                .set(OmsReceivable::getStatus, status)
                .set(OmsReceivable::getVerifyOpinion, verifyOpinion)
                .set(OmsReceivable::getModifiedName, userName)
                .set(OmsReceivable::getVerifyBy, UserUtil.getCode())
                .set(OmsReceivable::getVerifyName, userName)
                .set(OmsReceivable::getGmtVerify, LocalDateTime.now())
        );

        //获取收款单明细
        List<OmsReceivableItem> omsReceivableItems = omsReceivableItemService.list(new QueryWrapper<OmsReceivableItem>().lambda().eq(OmsReceivableItem::getReceivableCode, code));
        if (PASS.equals(status)) {
            omsReceivableItems.forEach(r -> {
                //确认费用结算信息
                omsReceivableDetailService.confirmSettlementStatus(r.getReceivableDetailCode(), BigDecimalUtil.getNumber(r.getThisSettledMoney()));
            });
        }
        if (NO_PASS.equals(status)) {
            omsReceivableItems.forEach(r -> {
                //还原更新费用信息
                omsReceivableDetailService.restoreSettlementStatus(r.getReceivableDetailCode(), BigDecimalUtil.getNumber(r.getThisSettledMoney()), BigDecimalUtil.getNumber(r.getReduceMoney()));
            });
        }


        return true;
    }

}
